{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "388ed25f",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from torch import nn,optim\n",
    "from torch.autograd import Variable\n",
    "from torchvision import datasets,transforms\n",
    "from torch.utils.data import DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "816b0d7c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 训练集\n",
    "train_dataset = datasets.MNIST(\n",
    "    root='./',  # 指定数据集存储的根目录。如果数据集已经存在，将从这里加载；如果不存在，将下载数据。\n",
    "    train=True,  # 设置为 True 表示加载训练集；如果为 False，则加载测试集。\n",
    "    transform=transforms.ToTensor(),  # 将图像数据转换为 PyTorch 张量格式，以便于后续处理和模型训练。\n",
    "    download=True  # 如果指定的 MNIST 数据集不存在，则下载数据集。\n",
    ")\n",
    "\n",
    "# 测试集\n",
    "test_dataset = datasets.MNIST(\n",
    "    root='./',  # 同样指定数据集存储的根目录。\n",
    "    train=False,  # 设置为 False 表示加载测试集；如果为 True，则加载训练集。\n",
    "    transform=transforms.ToTensor(),  # 将图像数据转换为 PyTorch 张量格式。\n",
    "    download=True  # 如果指定的 MNIST 数据集不存在，则下载数据集。\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "afee4807",
   "metadata": {},
   "outputs": [],
   "source": [
    "#批次大小\n",
    "batch_size=64\n",
    "\n",
    "#训练加载器\n",
    "train_loader=DataLoader(dataset=train_dataset,  # 使用之前定义的训练数据集\n",
    "                      batch_size=batch_size,\n",
    "                      shuffle=True)   #打乱数据集\n",
    "\n",
    "#测试加载器\n",
    "test_loader=DataLoader(dataset=test_dataset,   # 使用之前定义的测试数据集\n",
    "                      batch_size=batch_size,\n",
    "                      shuffle=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "cd20ecda",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([64, 1, 28, 28])\n",
      "torch.Size([64])\n"
     ]
    }
   ],
   "source": [
    "for i,data in enumerate(train_loader):   # 循环遍历训练数据加载器中的数据，i 是当前批次的索引，data 是当前批次的数据\n",
    "    inputs,labels=data     #解包当前批次的数据，将输入图像和标签分别赋值给 inputs 和 labels\n",
    "    print(inputs.shape)   #训练集的形状 64张图，一个通道，28*28的像素大小\n",
    "    print(labels.shape)   # 打印标签的形状，期望为 (64,) 或 (64, 1)，表示每个样本对应的标签\n",
    "    break "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "312beebc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([6, 8, 2, 6, 9, 1, 4, 7, 9, 9, 4, 9, 2, 8, 1, 2, 9, 5, 5, 1, 4, 9, 5, 3,\n",
       "        0, 8, 1, 4, 9, 8, 4, 5, 8, 9, 0, 1, 3, 6, 2, 3, 8, 9, 9, 9, 0, 1, 6, 1,\n",
       "        2, 0, 5, 7, 7, 0, 4, 9, 3, 7, 9, 6, 3, 5, 0, 2])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "34a3ecbc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "938"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(train_loader)   #每个批次64张图片，总共60000个图片，那需要测试938个批次"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "0c719ca6",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "#定义网络结构 \n",
    "class Net(nn.Module):  # 定义一个名为 Net 的神经网络类，继承自 nn.Module\n",
    "    def __init__(self):  # 构造函数，初始化网络结构\n",
    "        super(Net, self).__init__()  # 调用父类 nn.Module 的构造函数，确保正确初始化\n",
    "        self.conv1=nn.Sequential(nn.Conv2d(1,32,5,1,2),nn.ReLU(),nn.MaxPool2d(2,2))  #Conv2d (in_channels,out_channels,kernel_size,stride,padding,dilation)\n",
    "        self.conv2=nn.Sequential(nn.Conv2d(32,64,5,1,2),nn.ReLU(),nn.MaxPool2d(2,2))\n",
    "        self.fc1=nn.Sequential(nn.Linear(64*7*7,1000),nn.Dropout(p=0.5),nn.ReLU())\n",
    "        self.fc2=nn.Sequential(nn.Linear(1000,10),nn.Softmax(dim=1))\n",
    "    def forward(self, x):  # 定义前向传播方法，接收输入 x\n",
    "        # (64,1,28,28)\n",
    "        x=self.conv1(x)\n",
    "        x=self.conv2(x)\n",
    "        #(64,64,7,7)\n",
    "        x=x.view(x.size()[0],-1)\n",
    "        x=self.fc1(x)\n",
    "        x=self.fc2(x)\n",
    "        return x "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "054a19fc",
   "metadata": {},
   "outputs": [],
   "source": [
    "LR=0.001\n",
    "#定义模型\n",
    "model=Net()\n",
    "#定义代价函数\n",
    "mse_loss=nn.CrossEntropyLoss()\n",
    "#定义优化器\n",
    "optimizer=optim.Adam(model.parameters(),LR)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "faca8ece",
   "metadata": {},
   "outputs": [],
   "source": [
    "def train():\n",
    "    model.train() #模型的训练状态\n",
    "    for i,data in enumerate(train_loader):\n",
    "        #获得一个批次的数据和标签\n",
    "        inputs,labels=data\n",
    "        #获得模型的数据和标签\n",
    "        out=model(inputs)       \n",
    "        #交叉熵代价函数out(batch,C),labels(batch)\n",
    "        loss=mse_loss(out,labels)\n",
    "        #梯度清零\n",
    "        optimizer.zero_grad()\n",
    "        #计算梯度\n",
    "        loss.backward()\n",
    "        #修改权值\n",
    "        optimizer.step()\n",
    "        \n",
    "def test():\n",
    "    model.eval()  #模型的测试状态\n",
    "    correct=0  #正确数量\n",
    "    for i,data in enumerate(test_loader):\n",
    "        inputs,labels=data\n",
    "        out=model(inputs)\n",
    "        #获得最大值，以及最大值所在的位置\n",
    "        _,predicted=torch.max(out,1)\n",
    "        #预测正确的数量\n",
    "        correct+=(predicted==labels).sum()\n",
    "        \n",
    "    print(\"Test acc:{0}\".format(correct.item()/len(test_dataset)))\n",
    "        \n",
    "        \n",
    "    correct=0  #正确数量\n",
    "    for i,data in enumerate(train_loader):\n",
    "        inputs,labels=data\n",
    "        out=model(inputs)\n",
    "        #获得最大值，以及最大值所在的位置\n",
    "        _,predicted=torch.max(out,1)\n",
    "        #预测正确的数量\n",
    "        correct+=(predicted==labels).sum()\n",
    "        \n",
    "    print(\"Train acc:{0}\".format(correct.item()/len(train_dataset)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "e1a0567d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch: 0\n",
      "Test acc:0.9888\n",
      "Train acc:0.9896833333333334\n",
      "epoch: 1\n",
      "Test acc:0.9861\n",
      "Train acc:0.98905\n",
      "epoch: 2\n",
      "Test acc:0.9891\n",
      "Train acc:0.9908166666666667\n",
      "epoch: 3\n",
      "Test acc:0.9912\n",
      "Train acc:0.99295\n",
      "epoch: 4\n",
      "Test acc:0.9904\n",
      "Train acc:0.9909166666666667\n",
      "epoch: 5\n",
      "Test acc:0.9899\n",
      "Train acc:0.9896166666666667\n",
      "epoch: 6\n",
      "Test acc:0.988\n",
      "Train acc:0.98715\n",
      "epoch: 7\n",
      "Test acc:0.9895\n",
      "Train acc:0.9899333333333333\n",
      "epoch: 8\n",
      "Test acc:0.9922\n",
      "Train acc:0.99325\n",
      "epoch: 9\n",
      "Test acc:0.9903\n",
      "Train acc:0.9908\n",
      "epoch: 10\n",
      "Test acc:0.9894\n",
      "Train acc:0.9903333333333333\n",
      "epoch: 11\n",
      "Test acc:0.9886\n",
      "Train acc:0.9885\n",
      "epoch: 12\n",
      "Test acc:0.9902\n",
      "Train acc:0.9917333333333334\n",
      "epoch: 13\n",
      "Test acc:0.9889\n",
      "Train acc:0.9909166666666667\n",
      "epoch: 14\n",
      "Test acc:0.9905\n",
      "Train acc:0.99165\n",
      "epoch: 15\n",
      "Test acc:0.9888\n",
      "Train acc:0.9900666666666667\n",
      "epoch: 16\n",
      "Test acc:0.9876\n",
      "Train acc:0.9888666666666667\n",
      "epoch: 17\n",
      "Test acc:0.9879\n",
      "Train acc:0.98945\n",
      "epoch: 18\n",
      "Test acc:0.9865\n",
      "Train acc:0.9895333333333334\n",
      "epoch: 19\n",
      "Test acc:0.9878\n",
      "Train acc:0.98995\n"
     ]
    }
   ],
   "source": [
    "for epoch in range(20):\n",
    "    print('epoch:',epoch)\n",
    "    train()\n",
    "    test()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dbb3c3cf",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:yzk] *",
   "language": "python",
   "name": "conda-env-yzk-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.20"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
